package com.lcq.gulimall.order.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lcq.common.enume.OrderStatusEnum;
import com.lcq.common.exception.WareOutOfStockException;
import com.lcq.common.to.OrderItemLockTo;
import com.lcq.common.to.OrderSpuInfoTo;
import com.lcq.common.to.OrderTo;
import com.lcq.common.to.SecKillOrderTo;
import com.lcq.common.utils.PageUtils;
import com.lcq.common.utils.Query;
import com.lcq.common.utils.R;
import com.lcq.common.vo.MemberRsepVo;
import com.lcq.gulimall.order.constant.OrderConstant;
import com.lcq.gulimall.order.dao.OrderDao;
import com.lcq.gulimall.order.entity.OrderEntity;
import com.lcq.gulimall.order.entity.OrderItemEntity;
import com.lcq.gulimall.order.feign.CartFeignService;
import com.lcq.gulimall.order.feign.MemberFeignService;
import com.lcq.gulimall.order.feign.ProductFeignService;
import com.lcq.gulimall.order.feign.WareFeignService;
import com.lcq.gulimall.order.interceptor.UserLoginInterceptor;
import com.lcq.gulimall.order.service.OrderItemService;
import com.lcq.gulimall.order.service.OrderService;
import com.lcq.gulimall.order.to.OrderCreateTo;
import com.lcq.gulimall.order.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.weaver.ast.Or;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;


@Service("orderService")
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {
    @Autowired
    CartFeignService cartFeignService;

    @Autowired
    MemberFeignService memberFeignService;

    @Autowired
    WareFeignService wareFeignService;

    @Autowired
    ProductFeignService productFeignService;

    @Autowired
    ThreadPoolExecutor executor;

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    OrderItemService orderItemService;
    ThreadLocal<OrderSubmitVo> submitVoThreadLocal = new ThreadLocal<>();

    @Autowired
    RabbitTemplate rabbitTemplate;
    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public OrderConfirmVo getOrderConfirmInfo() {
        OrderConfirmVo orderConfirmVo = new OrderConfirmVo();
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        CompletableFuture<Void> orderItemsFuture = CompletableFuture.runAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            orderConfirmVo.setItems(cartFeignService.getCheckedCartItemList());
        }, executor);

        MemberRsepVo memberRsepVo = UserLoginInterceptor.userThreadLocal.get();
        orderConfirmVo.setIntegration(memberRsepVo.getIntegration());
        CompletableFuture<Void> addressFuture = CompletableFuture.runAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            orderConfirmVo.setAddress(memberFeignService.list(memberRsepVo.getId()));
        }, executor);

        CompletableFuture<Void> stocksFuture = orderItemsFuture.thenRunAsync(() -> {
            List<OrderItemVo> items = orderConfirmVo.getItems();
            if(items==null||items.size()==0){
                return;
            }

            List<Long> skuIds = items.stream().map(item -> {
                return item.getSkuId();
            }).collect(Collectors.toList());

            List<SkuStockVo> skuStockVos = wareFeignService.hasStocks(skuIds);
            if (skuStockVos!=null&&skuStockVos.size()>0){
                Map<Long, Boolean> map = skuStockVos.stream().collect(Collectors.toMap(SkuStockVo::getSkuId, SkuStockVo::getHasStock));
                orderConfirmVo.setStocks(map);
            }

        }, executor);

        try {
            CompletableFuture.allOf(stocksFuture,addressFuture).get();
        } catch (Exception e) {
            e.printStackTrace();
        }

        String token = UUID.randomUUID().toString();
        String key = getOrderTokenKey();
        redisTemplate.opsForValue().set(key,token);
        orderConfirmVo.setOrderToken(token);

        return orderConfirmVo;
    }

    private String getOrderTokenKey(){
        MemberRsepVo memberRsepVo = UserLoginInterceptor.userThreadLocal.get();
        return OrderConstant.TOKEN_SAVE_PREFIX+memberRsepVo.getId();
    }

    //@GlobalTransactional
    @Transactional
    @Override
    public SubmitOrderResponseVo submit(OrderSubmitVo submitVo) {
        SubmitOrderResponseVo responseVo = new SubmitOrderResponseVo();
        submitVoThreadLocal.set(submitVo);
        String orderToken = submitVo.getOrderToken();
        String key = getOrderTokenKey();
        String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
        Long result = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList(key), orderToken);
        if (result==0L){
            responseVo.setCode(1);
        }else {
            /**
             * 构建订单
             */
            responseVo.setCode(0);
            OrderCreateTo orderCreateTo = createOrder();

            /**
             * 锁库存
             */
            List<OrderItemEntity> orderItems = orderCreateTo.getOrderItems();
            lockWare(orderCreateTo);

            /**
             * 保存积分
             */
            //setCoupon();
            /**
             * 保存数据
             */
            this.save(orderCreateTo.getOrder());
            orderItemService.saveBatch(orderItems);
            OrderTo orderTo = new OrderTo();
            BeanUtils.copyProperties(orderCreateTo.getOrder(),orderTo);
            rabbitTemplate.convertAndSend("order-event-exchange","order.delay.queue",orderTo);
            responseVo.setOrderEntity(orderCreateTo.getOrder());
        }

        return responseVo;
    }

    @Override
    public OrderEntity getOrderBySn(String orderSn) {
        return this.getOne(new QueryWrapper<OrderEntity>().eq("order_sn",orderSn));
    }

    @Override
    public void cancelOrder(OrderTo orderTo) {
        OrderEntity order = this.getById(orderTo.getId());
        if (order==null){
            return;
        }

        if(!order.getStatus().equals(OrderStatusEnum.CREATE_NEW.getCode())){
            return;
        }

        order.setStatus(OrderStatusEnum.CANCELED.getCode());
        this.updateById(order);
        BeanUtils.copyProperties(order,orderTo);
        rabbitTemplate.convertAndSend("order-event-exchange","order.other.ware",orderTo);
    }

    @Override
    public void createSecKillOrder(SecKillOrderTo secKillOrderTo) {
        log.info("\n创建秒杀订单");
        OrderEntity entity = new OrderEntity();
        entity.setOrderSn(secKillOrderTo.getOrderSn());
        entity.setMemberId(secKillOrderTo.getMemberId());
        entity.setCreateTime(new Date());
        entity.setPayAmount(secKillOrderTo.getSeckillPrice());
        entity.setTotalAmount(secKillOrderTo.getSeckillPrice());
        entity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        entity.setPayType(1);
        // TODO 还有挺多的没设置
        BigDecimal price = secKillOrderTo.getSeckillPrice().multiply(new BigDecimal("" + secKillOrderTo.getNum()));
        entity.setPayAmount(price);

        this.save(entity);

        // 保存订单项信息
        OrderItemEntity itemEntity = new OrderItemEntity();
        itemEntity.setOrderSn(secKillOrderTo.getOrderSn());
        itemEntity.setRealAmount(price);
        itemEntity.setOrderId(entity.getId());
        itemEntity.setSkuQuantity(secKillOrderTo.getNum());
        itemEntity.setSkuId(secKillOrderTo.getSkuId());
        R info = productFeignService.spuInfo(secKillOrderTo.getSkuId());
        OrderSpuInfoTo spuInfoTo = info.getData(new TypeReference<OrderSpuInfoTo>() {});
        itemEntity.setSpuId(spuInfoTo.getSpuId());
        itemEntity.setSpuBrand(spuInfoTo.getSpuBrand());
        itemEntity.setSpuName(spuInfoTo.getSpuName());
        //itemEntity.setCategoryId(spuInfoTo.get);
        itemEntity.setGiftGrowth(secKillOrderTo.getSeckillPrice().multiply(new BigDecimal(secKillOrderTo.getNum())).intValue());
        itemEntity.setGiftIntegration(secKillOrderTo.getSeckillPrice().multiply(new BigDecimal(secKillOrderTo.getNum())).intValue());
        itemEntity.setPromotionAmount(new BigDecimal("0.0"));
        itemEntity.setCouponAmount(new BigDecimal("0.0"));
        itemEntity.setIntegrationAmount(new BigDecimal("0.0"));
        orderItemService.save(itemEntity);
    }

    private void setCoupon() {
        /**
         * 产生异常测试回滚事务
         */
        int a = 10/0;
    }

    private void lockWare(OrderCreateTo orderCreateTo) {

        OrderEntity order = orderCreateTo.getOrder();
        List<OrderItemEntity> orderItems = orderCreateTo.getOrderItems();
        if (orderItems==null||orderItems.size()==0){
            return;
        }

        List<OrderItemLockTo> lockTos = orderItems.stream().map(entity -> {
            OrderItemLockTo orderItemLockTo = new OrderItemLockTo();
            orderItemLockTo.setQuantity(entity.getSkuQuantity());
            orderItemLockTo.setSkuId(entity.getSkuId());
            return orderItemLockTo;
        }).collect(Collectors.toList());

        WareSkuLockVo wareSkuLockVo = new WareSkuLockVo();
        wareSkuLockVo.setOrderSn(order.getOrderSn());
        wareSkuLockVo.setLocks(lockTos);

        R r = wareFeignService.lockWare(wareSkuLockVo);
        Integer code = (Integer) r.get("code");
        if (code!=0){
            throw new WareOutOfStockException();
        }
    }

    private OrderCreateTo createOrder() {
        OrderCreateTo orderCreateTo = new OrderCreateTo();

        OrderEntity orderEntity = createOrderEntity();
        orderCreateTo.setOrder(orderEntity);

        List<OrderItemVo> cartItemList = cartFeignService.getCheckedCartItemList();
        List<OrderItemEntity> orderItemEntities = null;
        if (cartItemList!=null&&cartItemList.size()>0){
            orderItemEntities = cartItemList.stream().map(item -> {
                OrderItemEntity orderItemEntity = createOrderItemEntity(item);
                return orderItemEntity;
            }).collect(Collectors.toList());
        }


        orderCreateTo.setOrderItems(orderItemEntities);
//        orderCreateTo.setPayPrice();
//        orderCreateTo.setFare();
        return orderCreateTo;
    }

    private OrderItemEntity createOrderItemEntity(OrderItemVo item) {
        OrderItemEntity orderItemEntity = new OrderItemEntity();
        orderItemEntity.setSkuAttrsVals(StringUtils.collectionToDelimitedString(item.getSkuAttr(),","));
        orderItemEntity.setSkuName(item.getName());
        orderItemEntity.setSkuId(item.getSkuId());
        orderItemEntity.setSkuPic(item.getImage());
        orderItemEntity.setSkuPrice(item.getPrice());
        orderItemEntity.setSkuQuantity(item.getCount());
        R r = productFeignService.spuInfo(item.getSkuId());
        OrderSpuInfoTo spuInfoTo = r.getData(new TypeReference<OrderSpuInfoTo>() {});
        BeanUtils.copyProperties(spuInfoTo,orderItemEntity);
        return orderItemEntity;
    }

    private OrderEntity createOrderEntity() {
        OrderSubmitVo orderSubmitVo = submitVoThreadLocal.get();
        OrderEntity orderEntity = new OrderEntity();
        String timeId = IdWorker.getTimeId();
        orderEntity.setOrderSn(timeId);
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        R r = memberFeignService.fare(orderSubmitVo.getAddrId());
        FareVo fareVo = r.getData(new TypeReference<FareVo>() {});
        orderEntity.setPayAmount(orderSubmitVo.getPayPrice());
        orderEntity.setFreightAmount(fareVo.getFare());
        if(fareVo.getMemberAddressVo()!=null){
            orderEntity.setReceiverPhone(fareVo.getMemberAddressVo().getPhone());
            orderEntity.setReceiverProvince(fareVo.getMemberAddressVo().getProvince());
            orderEntity.setReceiverCity(fareVo.getMemberAddressVo().getCity());
            orderEntity.setReceiverRegion(fareVo.getMemberAddressVo().getRegion());
            orderEntity.setReceiverDetailAddress(fareVo.getMemberAddressVo().getDetailAddress());
        }

        return orderEntity;
    }




}